/*   Usage of printf:
 *   e Print a floating-point number in exponential notation, using lower-case letters
 *      The exponent always contains at least two digits
 *   E Same as 'e', but uses upper-case letters
 *   m Print the string corresponding to the specified value of the system *errno* variable
 *   u Print an unsigned integer
 *   x Print an integer as an unsigned hexadecimal number, using lower-case letters
 *   X Same as 'x', but uses upper-case letters
 *
 *   - Left-justify the number in the field (right justification is the default)
 *      Valid for %s and %c
 *   + Always print a plus or minus sign to indicate whether the number is positive or negative
 *      Valid for %d, %e, %E, %i
 *   # For %e, %E and %f, forces the number to include a decimal point, even if no digits follow
 *      For %x and %X, prefixes '0x' or '0X', respectively
 */

/*   Macro used in low level IO <fcntl.h>
 *   O_RDONLY  Open the file for read access
 *   O_WRONLY  Open the file for write access
 *   O_RDWR    Open the file for both read and write access
 *             Same as O_RDONLY | O_WRONLY
 *   O_READ    Same as O_RDWR (GNU systems only)
 *   O_WRITE   Same as O_WRONLY (GNU systems only)
 *   O_EXEC    Open the file for executing (GNU systems only)
 *   O_CREAT   The file will be created if it doesn't already exist
 *   O_EXCL    If O_CREAT is set as well, then 'open' fails if the specified file exists already
 *             Set this flag if you want to ensure you will not clobber an existing file
 *   O_TRUNC   Truncate the file to a length of zero bytes
 *   O_APPEND  Open the file for appending
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "iolib.h"

#define ARRAY_LENTH 8

static void fwrite_binary(FILE *stream);
static void fread_binary(FILE *stream, size_t size, size_t count);
static void getline_from_stdin(void);
static void sscanf_parameters(void);
static void grep_init_process(void);

int main(int argc, char *argv[], char *envp[]) {

    int  i, j, retval;
    char *filename;
    char *str;
    FILE *read_stream;
    FILE *write_stream;
    int  fd;

    /* Creat file */
    filename    = "read.dat";
    read_stream = safe_fopen(filename, "r");
    safe_fclose(read_stream, filename);

    /* Write file */
    filename = "write.dat";
    write_stream = safe_fopen(filename, "w");
    fprintf(write_stream, "hello world\n");
    safe_fclose(write_stream, filename);

    /* Write binary data to file */
    filename = "binary.dat";
    write_stream = safe_fopen(filename, "wb");
    fwrite_binary(write_stream);
    safe_fclose(write_stream, filename);

    /* Read binary data from file */
    filename = "binary.dat";
    read_stream = safe_fopen(filename, "rb");
    fread_binary(read_stream, sizeof(int), ARRAY_LENTH);
    safe_fclose(read_stream, filename);

    /* asprintf (mnemonic: allocating string print formatted)
     * Dynamically allocates the string to which it sends output
     * So the string will never overflow
     */
    asprintf(&str, "hello world %s\n", "shanghai");
    printf(str);

    /* Function getline is the preferred method for reading lines of text from a stream
     * Including standard input
     */
    getline_from_stdin();

    /* sscanf is prefered */
    sscanf_parameters();

    /* Ps and grep by popen */
    grep_init_process();

    /* Create file by low level function */
    filename = "create.dat";
    fd = safe_open(filename, O_WRONLY | O_CREAT | O_EXCL);
    str = "hello Taipei\n";
    write(fd, str, strlen(str) + 1);
    fsync(fd);
    safe_close(fd, filename);

    return EXIT_SUCCESS;
}

void fwrite_binary(FILE *stream) {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
    int arr_len = sizeof(arr) / sizeof(int); /* Note the usage */
    fwrite(arr, sizeof(int), arr_len, stream);
}

void fread_binary(FILE *stream, size_t size, size_t count) {
    size_t i;
    int *ptr = (int *) malloc(size * count);
    size_t n_read = fread(ptr, size, count, stream);
    if (n_read != count) {
        printf("Fail to read binary data from file (%m)");
        exit(EXIT_FAILURE);
    }
    printf("Read from file: ");
    for (i = 0; i < count; i++) {
        printf("[%d]: %d ", i, *(ptr + i));
    }
    printf("\n");
    free(ptr);
}

void getline_from_stdin() {
    size_t suggest_len = 10;
    char *str = (char *) malloc(suggest_len + 1); /* Note +1 */
    printf("Please input something: ");
    if (getline(&str, &suggest_len, stdin) == EOF) {
        printf("Fail to getline (%m)\n");
        exit(EXIT_FAILURE);
    }
    printf("Inputed: %s", str);
    free(str);
}

void sscanf_parameters() {
    size_t len = 10;
    char p1[512] = "";
    char p2[512] = "";
    char p3[512] = "";
    char *str = (char *) malloc(len + 1);
    printf("Please input 3 parameters (like: p1 p2 p3)\n");
    if (getline(&str, &len, stdin) == EOF) {
        printf("Fail to getline (%m)\n");
        exit(EXIT_FAILURE);
    }
    if (sscanf(str, "%s %s %s", p1, p2, p3) != 3) {
        printf("Fail to sscanf\n");
        exit(EXIT_FAILURE);
    }
    printf("3 parameters: %s, %s, %s\n", p1, p2, p3);
    free(str);
}

void grep_init_process() {
    FILE *ps_pipe       = NULL;
    FILE *grep_pipe     = NULL;
    size_t read_nbytes  = 1024;
    char *ps_output_str = NULL;
    ps_output_str = (char *) malloc(read_nbytes + 1);
    ps_pipe   = popen("ps -A", "r");
    grep_pipe = popen("grep -i bash", "w"); /* Note the option r and w in both case */
    if ((!ps_pipe) || (!grep_pipe)) {
        printf("Fail to open pipe (%m)\n");
        exit(EXIT_FAILURE);
    }
    /* \0 in order to print all */
    if (getdelim(&ps_output_str, &read_nbytes, '\0', ps_pipe) == EOF) {
        printf("Fail to get output from pipe (%m)\n");
        exit(EXIT_FAILURE);
    }
    if (pclose(ps_pipe) != 0) {
        printf("Fail to close ps pipe (%m)\n");
        exit(EXIT_FAILURE);
    }
    fprintf(grep_pipe, "%s\n", ps_output_str);
    if (pclose(grep_pipe) != 0) {
        printf("Fail to close grep pipe (%m)\n");
        exit(EXIT_FAILURE);
    }
    free(ps_output_str);
}
